#include <Arduino.h>
#include <Wire.h>
#include <VL53L1X.h>
#include <LTR308.h>
#include <Adafruit_LIS3DH.h>
#include <HardwareSerial.h>
#include <ESP32Time.h>
#include <ArduinoJson.h>
#include <WiFi.h>
#include <WiFiClient.h>
#include <WebServer.h>
#include <ESPmDNS.h>
#include <Update.h>
#include <Preferences.h>

//http://update.local 172.20.10.12

const String SOFT_VERSION = "1.3.3";

const char* host = "update";
const char* ssid = "esp32";
const char* password = "12345678";

const String ALI_PRODUCTKEY = "a11HKzRAgdA";
String ALI_DEVICESECRET = "";
String ALI_DEVICENAME = "";

String imei_device_info[][2] = {
  {"865447068996567", "84e3a9b0cc4ee07ac441b2d6ae55b124"},
  {"865447068750535", "40bc83f05fd23b1da2fc8d0f38759334"},
  {"865447068960498", "acf0f9f9a5ade1439074e93d45f84149"},
  {"865447068997284", "92b9160c28f7049a79c2465318ce01c2"},
  {"", ""},
};

/* 定义间歇性休眠的启用和时间 */
#define CONFIG_INTERMIT_SLEEP 1
#define CONFIG_LATENIGHT_SLEEP 1
#define CONFIG_INTERMIT_TIMER 10000  /*ms*/
#define CONFIG_LIGHTSLEEP_TEMER 300  /*second*/
/* 1 蜂鸣器静音 不再对异常情况进行通知*/
#define CONFIG_BEE_SILENT 0

#define MAX_ERR_COUNT 30
#define SERIOUSFAULT_COUNT 20
#define wdtTimeout 6000

const gpio_num_t VL_INT_PIN  = GPIO_NUM_2;
const gpio_num_t VL_SHUT_PIN = GPIO_NUM_1;
const gpio_num_t GS_INT_PIN  = GPIO_NUM_6;
const gpio_num_t ALS_INT_PIN = GPIO_NUM_8;
const gpio_num_t BAT_ADC_PIN = GPIO_NUM_0;
const gpio_num_t EC_DTR_PIN  = GPIO_NUM_3;
const gpio_num_t BEEPER_PIN  = GPIO_NUM_10;

bool latenight_sleep_flag = false;
bool fault_vl_device = false;
uint8_t ec_fault_count = 0;

Preferences preferences;

WebServer server(80);

String ec_uart_interrupt_buf = "";

/* FREE RTOS */
TaskHandle_t TaskUartHandle = NULL;
TaskHandle_t TaskMQTTHandle = NULL;
TimerHandle_t xBEEZTimer;
TimerHandle_t xInfoPushTimer;
TimerHandle_t xLightSleepTimer;

hw_timer_t *watchdog_timer = NULL;

#define EVENT_ATOK_FLAG     (1 << 0)
#define EVENT_ATERR_FLAG    (1 << 1)
#define EVENT_ATSTAT_FLAG   (1 << 2)
#define EVENT_ATRECV_FLAG   (1 << 3)
#define EVENT_ATPING_FLAG   (1 << 4)
#define EVENT_ATOPEN_FLAG   (1 << 5)
#define EVENT_ATCONN_FLAG   (1 << 6)
#define EVENT_TIMEDRUN_FLAG (1 << 7)
#define EVENT_REPLY_FLAG    (1 << 8)
#define EVENT_FINDMY_FLAG   (1 << 9)
#define EVENT_UNNOWN_FLAG   (1 << 10)
/* END FREE RTOS */

volatile uint32_t gs_click_count = 0;

String IMEI = "";
String RSSI = "";

ESP32Time rtc;
HardwareSerial MySerial(1);
VL53L1X sensor;
LTR308 light;

void Taskmqtt(void *pvParameters);
void TaskUart(void *pvParameters);

#define CLICKTHRESHOLD 120

Adafruit_LIS3DH lis = Adafruit_LIS3DH();
//Window Factor
const static float window_factor = 3.67;
// If gain = 0, device is set to 1X gain
// If gain = 1, device is set to 3X gain (default)
// If gain = 2, device is set to 6X gain
// If gain = 3, device is set to 9X gain
// If gain = 4, device is set to 18X gain
const static unsigned char gain = 1;
//------------------------------------------------------
// If integrationTime = 0, integrationTime will be 400ms / 20 bits result
// If integrationTime = 1, integrationTime will be 200ms / 19 bits result 
// If integrationTime = 2, integrationTime will be 100ms / 18 bits result (default)
// If integrationTime = 3, integrationTime will be 50ms / 17 bits result 
// If integrationTime = 4, integrationTime will be 25ms / 16 bits result 
const static unsigned char integrationTime = 2;
//------------------------------------------------------
// If measurementRate = 0, measurementRate will be 25ms
// If measurementRate = 1, measurementRate will be 50ms
// If measurementRate = 2, measurementRate will be 100ms (default)
// If measurementRate = 3, measurementRate will be 500ms
// If measurementRate = 5, measurementRate will be 1000ms
// If measurementRate = 6, measurementRate will be 2000ms
// If measurementRate = 7, measurementRate will be 2000ms
const static unsigned char measurementRate = 2;

const char* loginIndex =
 "<form name='loginForm'>"
    "<table width='20%' bgcolor='A09F9F' align='center'>"
        "<tr>"
            "<td colspan=2>"
                "<center><font size=4><b>ESP32 Login Page</b></font></center>"
                "<br>"
            "</td>"
            "<br>"
            "<br>"
        "</tr>"
        "<tr>"
             "<td>Username:</td>"
             "<td><input type='text' size=25 name='userid'><br></td>"
        "</tr>"
        "<br>"
        "<br>"
        "<tr>"
            "<td>Password:</td>"
            "<td><input type='Password' size=25 name='pwd'><br></td>"
            "<br>"
            "<br>"
        "</tr>"
        "<tr>"
            "<td><input type='submit' onclick='check(this.form)' value='Login'></td>"
        "</tr>"
    "</table>"
"</form>"
"<script>"
    "function check(form)"
    "{"
    "if(form.userid.value=='admin' && form.pwd.value=='admin')"
    "{"
    "window.open('/serverIndex')"
    "}"
    "else"
    "{"
    " alert('Error Password or Username')/*displays error message*/"
    "}"
    "}"
"</script>";

/*
 * Server Index Page
 */

const char* serverIndex =
"<script src='https://ajax.googleapis.com/ajax/libs/jquery/3.2.1/jquery.min.js'></script>"
"<form method='POST' action='#' enctype='multipart/form-data' id='upload_form'>"
   "<input type='file' name='update'>"
        "<input type='submit' value='Update'>"
    "</form>"
 "<div id='prg'>progress: 0%</div>"
 "<script>"
  "$('form').submit(function(e){"
  "e.preventDefault();"
  "var form = $('#upload_form')[0];"
  "var data = new FormData(form);"
  " $.ajax({"
  "url: '/update',"
  "type: 'POST',"
  "data: data,"
  "contentType: false,"
  "processData:false,"
  "xhr: function() {"
  "var xhr = new window.XMLHttpRequest();"
  "xhr.upload.addEventListener('progress', function(evt) {"
  "if (evt.lengthComputable) {"
  "var per = evt.loaded / evt.total;"
  "$('#prg').html('progress: ' + Math.round(per*100) + '%');"
  "}"
  "}, false);"
  "return xhr;"
  "},"
  "success:function(d, s) {"
  "console.log('success!')"
 "},"
 "error: function (a, b, c) {"
 "}"
 "});"
 "});"
 "</script>";

void beep(int times, int duration, int interval) {
#if (CONFIG_BEE_SILENT == 0)
  for (int i = 0; i < times; i++) {
    digitalWrite(BEEPER_PIN, HIGH);
    delay(duration);
    digitalWrite(BEEPER_PIN, LOW);
    delay(interval);
  }
#endif
}

void ota_updata(void)
{
	byte wifi_retry_count = 0;
  Serial.println("For OTA Connect to WIFI");
	WiFi.begin(ssid, password);
	while (WiFi.status() != WL_CONNECTED) {
		delay(500);
		if (wifi_retry_count++ >= 16)break;
	}

  Serial.println("");
  Serial.print("Connected to ");
  Serial.println(ssid);
  Serial.print("IP address: ");
  Serial.println(WiFi.localIP());

	if (WiFi.status() == WL_CONNECTED) {
    beep(2, 100, 100);
		if (!MDNS.begin(host)) { //http://update.local 172.20.10.12
			while (1);
		}
		server.on("/", HTTP_GET, []() {
		server.sendHeader("Connection", "close");
		server.send(200, "text/html", loginIndex);
		});
		server.on("/serverIndex", HTTP_GET, []() {
			server.sendHeader("Connection", "close");
			server.send(200, "text/html", serverIndex);
		});
		/*handling uploading firmware file */
		server.on("/update", HTTP_POST, []() {
			server.sendHeader("Connection", "close");
			server.send(200, "text/plain", (Update.hasError()) ? "FAIL" : "OK");
      beep(3, 50, 50);
			ESP.restart();
		}, []() {
			HTTPUpload& upload = server.upload();
			if (upload.status == UPLOAD_FILE_START) {
			Serial.printf("Update: %s\n", upload.filename.c_str());
			if (!Update.begin(UPDATE_SIZE_UNKNOWN)) { //start with max available size
				Update.printError(Serial);
			}
			} else if (upload.status == UPLOAD_FILE_WRITE) {
			/* flashing firmware to ESP*/
			if (Update.write(upload.buf, upload.currentSize) != upload.currentSize) {
				Update.printError(Serial);
			}
			} else if (upload.status == UPLOAD_FILE_END) {
			if (Update.end(true)) { //true to set the size to the current progress
				Serial.printf("Update Success: %u\nRebooting...\n", upload.totalSize);
			} else {
				Update.printError(Serial);
			}
			}
		});
		server.begin();
		MDNS.addService("http", "tcp", 80);
		while (1) {
			server.handleClient();
			delay(10);
		}
	}
	WiFi.disconnect(true, true);
  WiFi.mode(WIFI_OFF); 
	MDNS.end();
	server.stop();
}

void printError(byte error) {
  Serial.print("I2C error: ");
  Serial.print(error, DEC);
  Serial.print(", ");
  
  switch(error) {
    case 0: Serial.println("success"); break;
    case 1: Serial.println("data too long for transmit buffer"); break;
    case 2: Serial.println("received NACK on address (disconnected?)"); break;
    case 3: Serial.println("received NACK on data"); break;
    case 4: Serial.println("other error"); break;
    default: Serial.println("unknown error");
  }
}

void vl_ic_check()
{
  digitalWrite(VL_SHUT_PIN, HIGH);
  delay(10);
  sensor.setTimeout(500);
  if (!sensor.init()) {
    Serial.println("VL53L1X Failed to detect and initialize sensor!");
    beep(5, 100, 100);
    ESP.restart();
  }
  digitalWrite(VL_SHUT_PIN, LOW);
}

void vl_shutdown() {
  digitalWrite(VL_SHUT_PIN, LOW);
}

void vl_init() {
  digitalWrite(VL_SHUT_PIN, HIGH);
  vTaskDelay(10 / portTICK_PERIOD_MS); 
  sensor.setTimeout(500);
  if (!sensor.init()) {
    Serial.println("VL53L1X Failed to detect and initialize sensor!");
    beep(3, 100, 100);
    fault_vl_device = true;
    digitalWrite(VL_SHUT_PIN, LOW);
    return;
  } else {
    fault_vl_device = false;
  }
  //Serial.println("VL53L1X Init");
  sensor.setDistanceMode(VL53L1X::Long);
  sensor.setMeasurementTimingBudget(200000);
  sensor.startContinuous(200);
}

void light_init() {
  unsigned char ID;
  light.begin();
  // if (light.getPartID(ID)) {
  //   Serial.print("LTR308 Init Part ID: 0X");
  //   Serial.println(ID, HEX);
  // } else {
  //   printError(light.getError());
  // }
  vTaskDelay(10 / portTICK_PERIOD_MS); 
  light.setPowerUp();
  light.setGain(gain);
  light.setMeasurementRate(integrationTime, measurementRate);
}

void light_shutdown() {
  light.setPowerDown();
}

void readVLData() {
  if (fault_vl_device == true)return;
  sensor.read();
  Serial.print("range: ");
  Serial.print(sensor.ranging_data.range_mm);
  Serial.print("\tstatus: ");
  Serial.print(VL53L1X::rangeStatusToString(sensor.ranging_data.range_status));
  Serial.print("\tpeak signal: ");
  Serial.print(sensor.ranging_data.peak_signal_count_rate_MCPS);
  Serial.print("\tambient: ");
  Serial.println(sensor.ranging_data.ambient_count_rate_MCPS);
  Serial.println();
  if (sensor.ranging_data.peak_signal_count_rate_MCPS < 0.15) {
    sensor.ranging_data.range_mm = 9999;
  }
  vl_shutdown();
}

double readLuxData() {
  unsigned long rawData;
  if (light.getData(rawData)) {
    double lux;
    boolean good = light.getLux(gain, integrationTime, rawData, lux);
    lux *= window_factor;
    Serial.print("Lux: ");
    Serial.print(lux);
    if (good) Serial.println(" (valid data)"); 
    else Serial.println(" (BAD)");
    light_shutdown();
    return lux;
  } else {
    printError(light.getError());
    return 0;
  }
}

void ARDUINO_ISR_ATTR resetModule() {
  ets_printf("reboot\n");
  esp_restart();
}

// void ARDUINO_ISR_ATTR handle_VL_INT() {
//   interruptVLFlag = true;
// }

void ARDUINO_ISR_ATTR handle_GS_INT() {
  if (digitalRead(GS_INT_PIN)) {
    gs_click_count+=1;
  }
}

void ARDUINO_ISR_ATTR onSerialData() {
  volatile size_t ec_uart_received_bytes = MySerial.available();;
  size_t max_recv_bytes = ec_uart_received_bytes;

  while (ec_uart_received_bytes --) {
    char buf = (char)MySerial.read();
    ec_uart_interrupt_buf += buf;
  }
  // Serial.println("****ORAGIN****");
  // Serial.print(ec_uart_interrupt_buf);
  // Serial.println("****END****");

  BaseType_t xHigherPriorityTaskWoken = pdFALSE;
  vTaskNotifyGiveFromISR(TaskUartHandle, &xHigherPriorityTaskWoken);
  portYIELD_FROM_ISR();
}

const char *uartErrorStrings[] = {
  "UART_NO_ERROR",
  "UART_BREAK_ERROR",
  "UART_BUFFER_FULL_ERROR",
  "UART_FIFO_OVF_ERROR",
  "UART_FRAME_ERROR",
  "UART_PARITY_ERROR"
};
void ARDUINO_ISR_ATTR onSerialErr(hardwareSerial_error_t err) {
  Serial.printf("\n-- onReceiveError [ERR#%d:%s] \n", err, uartErrorStrings[err]);
  Serial.printf("-- onReceiveError:: There are %d bytes available.\n", MySerial.available());
  volatile size_t ec_uart_received_bytes = MySerial.available();;
  size_t max_recv_bytes = ec_uart_received_bytes;

  while (ec_uart_received_bytes --) {
    char buf = (char)MySerial.read();
    ec_uart_interrupt_buf += buf;
  }
  // Serial.println("****ORAGIN****");
  // Serial.print(ec_uart_interrupt_buf);
  // Serial.println("****END****");

  BaseType_t xHigherPriorityTaskWoken = pdFALSE;
  vTaskNotifyGiveFromISR(TaskUartHandle, &xHigherPriorityTaskWoken);
  portYIELD_FROM_ISR();
}

bool isWithinRange(double new_value, double old_value, float range) {
  return (abs(new_value - old_value) > range);
}

#define R1 75000.0  // 电阻R1的阻值，单位为欧姆
#define R2 24000.0  // 电阻R2的阻值，单位为欧姆
#define NUM_SAMPLES 100
uint32_t getStableAnalogValue(int pin) {
  long sum = 0;
  for (int i = 0; i < NUM_SAMPLES; i++) {
    sum += analogReadMilliVolts(pin);  // 读取多次ADC值
    delay(1);  // 可选，给ADC一个短暂的时间进行稳定
  }
  return sum / NUM_SAMPLES;  // 返回平均值 
}

float readVBAT() {
  uint32_t voltage = getStableAnalogValue(BAT_ADC_PIN);
  float vinVolts = (voltage * (R1 + R2) / R2) / 1000;
  return vinVolts;
}

void ec_low_power_mode() {
  digitalWrite(EC_DTR_PIN, LOW);
}

void ec_standard_mode() {
  digitalWrite(EC_DTR_PIN, HIGH);
  vTaskDelay(600 / portTICK_PERIOD_MS); 
}

void check_vbat_voltage() {
  float voltage = readVBAT();
  int beepTimes = 0;
  if (voltage < 2.7) {
    beepTimes = 1;
  } else if (voltage < 3.2) {
    beepTimes = 2;
  } else if (voltage < 3.7) {
    beepTimes = 3;
  } else if (voltage <= 5.0) {
    beepTimes = 4;
  }
  beep(beepTimes, 200, 200);
}

void sendATCommand(String command) {
  MySerial.println(command);
}

void handle_mqtt_push(String data) {
  String topic = "/sys/" + ALI_PRODUCTKEY + "/" + ALI_DEVICENAME + "/thing/event/property/post";
  sendATCommand("AT+QMTPUBEX=0,0,0,0,\"" + topic + "\"," + data.length());
  vTaskDelay(20 / portTICK_PERIOD_MS);
  sendATCommand(data);
}

String handle_mqtt_json_generator(String A, String B)
{
  JsonDocument doc;
  doc.clear();
  JsonObject params = doc["params"].to<JsonObject>();
  params[A] = B;

  String jsonstr;
  serializeJson(doc, jsonstr);
  return jsonstr;
}

String handle_mqtt_json_generator(String A, float B) {
  JsonDocument doc;
  doc.clear();
  JsonObject params = doc["params"].to<JsonObject>();
  params[A] = B;
  String jsonstr;
  serializeJson(doc, jsonstr);
  return jsonstr;
}

int SendandCheckAT(String cmd, TickType_t timeout, String userInfo)
{
  uint32_t ulNotificationValue;
  vTaskDelay(200 / portTICK_PERIOD_MS); 
  sendATCommand(cmd);
  if (xTaskNotifyWait(0, ULONG_MAX, &ulNotificationValue, pdMS_TO_TICKS(timeout)) == pdFAIL) {
    Serial.println(userInfo + " TIMEOUT");
    return -2;
  } else {
    if (ulNotificationValue & EVENT_ATOK_FLAG) {
      Serial.println(userInfo + " OK");
      return 0;
    } else if (ulNotificationValue & EVENT_ATERR_FLAG) {
      Serial.println(userInfo + " ERR");
      return -1;
    }
  }  
  return -1;
}

int SendandCheckMQTT(String cmd, String data, TickType_t timeout, String userInfo, uint8_t *errcount)
{
  uint32_t ulNotificationValue;
  int ret = -1;
  uint8_t count = 0;
  do {
    handle_mqtt_push(handle_mqtt_json_generator(cmd, data));
    if (xTaskNotifyWait(0, ULONG_MAX, &ulNotificationValue, pdMS_TO_TICKS(timeout)) == pdFAIL) {
      Serial.println(userInfo + " PUSH TIMEOUT");
      ret = -2;
    } else {
      if (ulNotificationValue & EVENT_ATOK_FLAG) {
        Serial.println(userInfo + " PUSH OK");
        ret = 0;
      } else if (ulNotificationValue & EVENT_ATERR_FLAG) {
        Serial.println(userInfo + " PUSH ERR");
        ret = -1;
      }
    }
    vTaskDelay(200 / portTICK_PERIOD_MS);

    if (ret != 0) {
      *errcount+=1;
      vTaskDelay(4000 / portTICK_PERIOD_MS);
    }else *errcount = 0;

    if (count >= MAX_ERR_COUNT)return ret;
    else count++;

  } while (ret != 0);
  return ret;
}

int SendandCheckMQTT(String cmd, float data, TickType_t timeout, String userInfo, uint8_t *errcount)
{
  uint32_t ulNotificationValue;
  int ret = -1;
  uint8_t count = 0;
  do {
    handle_mqtt_push(handle_mqtt_json_generator(cmd, data));
    if (xTaskNotifyWait(0, ULONG_MAX, &ulNotificationValue, pdMS_TO_TICKS(timeout)) == pdFAIL) {
      Serial.println(userInfo + " PUSH TIMEOUT");
      ret = -2;
    } else {
      if (ulNotificationValue & EVENT_ATOK_FLAG) {
        Serial.println(userInfo + " PUSH OK");
        ret = 0;
      } else if (ulNotificationValue & EVENT_ATERR_FLAG) {
        Serial.println(userInfo + " PUSH ERR");
        ret = -1;
      }
    }
    vTaskDelay(200 / portTICK_PERIOD_MS);

    if (ret != 0) {
      *errcount+=1;
      vTaskDelay(4000 / portTICK_PERIOD_MS);
    }else *errcount = 0;

    if (count >= MAX_ERR_COUNT)return ret;
    else count++;

  } while (ret != 0);
  return ret;
}


void timerPushMQTT(bool wakeup)
{
  static double lux_data, old_lux_data;
  static float vbat, old_vbat;
  static uint16_t old_range_mm;
  static uint32_t old_gs_click_count = 0;

  vl_init();
  light_init();
  ec_standard_mode();
  vbat = readVBAT();
  lux_data = readLuxData();
  readVLData();
  Serial.print("gs_click_count :");
  Serial.println(gs_click_count);

  if (isWithinRange(vbat, old_vbat, 0.2) || wakeup) {
    old_vbat = vbat;
    SendandCheckMQTT("Battery", vbat, 1000, "Battery", &ec_fault_count);
  }

  if (isWithinRange(sensor.ranging_data.range_mm, old_range_mm, 100) || wakeup) {
    SendandCheckMQTT("DetectDistance", sensor.ranging_data.range_mm, 1000, "DetectDistance", &ec_fault_count);
  }
  if (isWithinRange(sensor.ranging_data.range_mm, old_range_mm, 500) && !wakeup) {
    SendandCheckMQTT("proximity_trig", 1, 1000, "proximity_trig", &ec_fault_count);
  }
  old_range_mm = sensor.ranging_data.range_mm;

  if (isWithinRange(lux_data, old_lux_data, 20) || wakeup) {
    SendandCheckMQTT("lux", lux_data, 1000, "lux", &ec_fault_count);
  }
  if (isWithinRange(lux_data, old_lux_data, 1000) && !wakeup) {
    SendandCheckMQTT("light_trig", 1, 1000, "light_trig", &ec_fault_count);
  }
  old_lux_data = lux_data;

  if (isWithinRange(gs_click_count, old_gs_click_count, 1) || wakeup) {
    old_gs_click_count = gs_click_count;
    SendandCheckMQTT("IllegalMoveWarning", gs_click_count, 1000, "IllegalMoveWarning", &ec_fault_count);
  }
  ec_low_power_mode();
}

void net_config_AT()
{
  static bool first_boot = true;
  uint32_t ulNotificationValue;
  if (first_boot == true) {
    first_boot = false;
    retry:
    SendandCheckAT("AT+GSN", 1000, "AT+GSN");
    SendandCheckAT("AT+QLTS=2", 1000, "AT+QLTS=2");
    SendandCheckAT("AT+CSQ", 1000, "AT+CSQ");

    SendandCheckAT("AT+QMTCLOSE=0", 1000, "AT+QMTCLOSE");
    SendandCheckAT("AT+QMTDISC=0", 1000, "AT+QMTDISC");
    SendandCheckAT("AT+QMTCFG=\"recv/mode\",0,0,1", 1000, "AT+QMTCFG/RECV");
    SendandCheckAT("AT+QMTCFG=\"aliauth\",0,\"" + ALI_PRODUCTKEY + "\",\"" + ALI_DEVICENAME + "\",\"" + ALI_DEVICESECRET + "\"", 1000, "AT+QMTCFG/ALI");
    SendandCheckAT("AT+QMTOPEN=0,\"" + ALI_PRODUCTKEY + ".iot-as-mqtt.cn-shanghai.aliyuncs.com\",1883", 1000, "AT+QMTOPEN");
    if (SendandCheckAT("AT+QMTCONN=0,\"clientExample\"", 1000, "AT+QMTCONN")) {
      goto retry;
    }
    SendandCheckAT("AT+QMTSUB=0,1,\"/sys/" + ALI_PRODUCTKEY + "/" + ALI_DEVICENAME + "/thing/service/property/set\",1", 1000, "AT+QMTSUB");

    SendandCheckMQTT("FirmwareVersion", SOFT_VERSION, 1000, "FirmwareVersion", &ec_fault_count);
    SendandCheckMQTT("Battery", readVBAT(), 1000, "Battery", &ec_fault_count);
    SendandCheckMQTT("StartTime", rtc.getTime("%F %X"), 1000, "StartTime", &ec_fault_count);
    SendandCheckMQTT("GSM", RSSI.toFloat(), 1000, "GSM", &ec_fault_count);
    
    SendandCheckAT("AT+QSCLK=1", 1000, "AT+QSCLK");

    timerPushMQTT(true);
  }
}

void handle_mqtt_request(String data) {
  JsonDocument doc;
  while (true) {
    int startIndex = data.indexOf("{");
    int endIndex = data.indexOf("}\"", startIndex);

    // 如果没有找到完整的 JSON 数据，退出循环
    if (startIndex == -1 || endIndex == -1) {
      break;
    }
    doc.clear();
    // 提取并解析 JSON 数据
    String handleData = data.substring(startIndex, endIndex + 1);
    DeserializationError error = deserializeJson(doc, handleData);
    if (error) {
      Serial.print(F("deserializeJson() failed: "));
      Serial.println(error.f_str());
      return;
    }

    // 处理 JSON 数据
    if (doc["params"].containsKey("BEEP")) {
      xTaskNotify(TaskMQTTHandle, EVENT_FINDMY_FLAG, eSetBits);
    } else if (doc["params"].containsKey("devicewakeup")) {
      xTaskNotify(TaskMQTTHandle, EVENT_REPLY_FLAG, eSetBits);
    } else if (doc["params"].containsKey("powerstate")) {
      xTaskNotify(TaskMQTTHandle, EVENT_FINDMY_FLAG, eSetBits);
    } else {
      Serial.println("JsonDocument Analysis error");
    }

    // 打印调试信息
    // Serial.println("Original Buffer: ");
    // Serial.println(data);
    Serial.println("Extracted JSON: ");
    Serial.println(handleData);
    // 移除已处理的 JSON 数据
    data.remove(0, endIndex + 1);
  }
}

//example: "+QMTOPEN: 0,3" -> 3
int extractedNumberAT(String data)
{
  int commaIndex = data.lastIndexOf(',');
  if (commaIndex == -1)
    return -1;
  return data.substring(commaIndex + 1).toInt();
}

String extractedGSNAT(String data)
{
  int startIndex = data.indexOf('\n') + 1;  // 加1以跳过第一个 '\r'
  int endIndex = data.indexOf('\r', startIndex);
  if (startIndex == -1 || endIndex == -1) {
    return "";
  }
  return data.substring(startIndex, endIndex);
}

String extractedCSQAT(String data)
{
  int startIndex = data.indexOf(':') + 2;
  int endIndex = data.indexOf(',', startIndex);
  if (startIndex == -1 || endIndex == -1) {
    return "";
  }
  return data.substring(startIndex, endIndex);
}

void extractedQLTSAT(String data)
{
  int startIndex = data.indexOf("\"") + 1;
  int endIndex = data.indexOf("\"", startIndex);
  String handlData = data.substring(startIndex, endIndex);
  if (startIndex == -1 || endIndex == -1) {
    return;
  }

  // 提取年份、月份、日期、小时、分钟、秒数
  String year = handlData.substring(0, 4);
  String month = handlData.substring(5, 7);
  String day = handlData.substring(8, 10);
  String hour = handlData.substring(11, 13);
  String minute = handlData.substring(14, 16);
  String second = handlData.substring(17, 19);

  if (year.equals("2070")) {
    latenight_sleep_flag = false;
  } else {
    latenight_sleep_flag = true;
  }

  rtc.setTime(second.toInt(), minute.toInt(), hour.toInt(), day.toInt(), month.toInt(), year.toInt());
  Serial.println(rtc.getTime("%F %X"));
}

void enterLightSleep(uint64_t wakeup_time_us) {
  //const int64_t wakeup_time_us = 7 * 3600 * 1000000LL;  // 7小时后唤醒
  Serial.println("Entering Light Sleep...");
  detachInterrupt(digitalPinToInterrupt(GS_INT_PIN));

  esp_sleep_enable_timer_wakeup(wakeup_time_us);

  gpio_wakeup_enable(GPIO_NUM_19, GPIO_INTR_LOW_LEVEL);
  gpio_wakeup_enable(GS_INT_PIN, GPIO_INTR_HIGH_LEVEL);
  esp_sleep_enable_gpio_wakeup();
  vTaskDelay(20 / portTICK_PERIOD_MS); 
  esp_light_sleep_start();
  Serial.println("Woke up from Light Sleep!");
  
  attachInterrupt(digitalPinToInterrupt(GS_INT_PIN), handle_GS_INT, RISING);
}

void vBEEZTimerCallback(TimerHandle_t xTimer) {
  static volatile int toggleCount = 0; // 用于跟踪蜂鸣器开关次数
  // 切换蜂鸣器状态
  digitalWrite(BEEPER_PIN, !digitalRead(BEEPER_PIN));

  // 计数增加
  toggleCount++;

  // 如果已经达到20次（10次开10次关），停止定时器
  if (toggleCount >= 40) { // 20次开关各算一次，所以是40
    toggleCount = 0;
    xTimerStopFromISR(xTimer, 0);
    digitalWrite(BEEPER_PIN, LOW);  // 确保蜂鸣器最后关闭
  }
}

void vInfoPushTimerCallback(TimerHandle_t xTimer)
{
  BaseType_t xHigherPriorityTaskWoken = pdFALSE;
  xTaskNotifyFromISR(TaskMQTTHandle, EVENT_TIMEDRUN_FLAG, eSetBits, &xHigherPriorityTaskWoken);
}

void vLightSleepTimerCallback(TimerHandle_t xTimer)
{
  xTimerStopFromISR(xTimer, 0);
  enterLightSleep(CONFIG_LIGHTSLEEP_TEMER * 1000000LL);
}

void nvs_fault_clear(void)
{
  preferences.begin("storage", false);
  preferences.putUInt("fault", 0);
  preferences.end();
  Serial.println("nvs_fault_clear: done");
}

uint32_t nvs_fault_up(void)
{
  uint32_t fault_count = 0;
  preferences.begin("storage", false);
  fault_count = preferences.getUInt("fault");
  fault_count++;
  preferences.putUInt("fault", fault_count);
  preferences.end(); 
  Serial.print("nvs_fault_up: fault_count - ");
  Serial.println(fault_count);
  return fault_count;
}

void setup() {
  delay(1000);  // 等待板子初始化
  esp_reset_reason_t rst_rea = esp_reset_reason();
  pinMode(EC_DTR_PIN, OUTPUT);
  digitalWrite(EC_DTR_PIN, HIGH);
  pinMode(BEEPER_PIN, OUTPUT);
  digitalWrite(BEEPER_PIN, LOW);
  pinMode(VL_INT_PIN, INPUT);
  Serial.begin(1500000);
  Serial.println("Hello IOT_SENSOR " + SOFT_VERSION);
  ota_updata();

  MySerial.begin(115200, SERIAL_8N1, 19, 18);
  Wire.begin(5, 4, 100000);

  // attachInterrupt(digitalPinToInterrupt(VL_INT_PIN), handle_VL_INT, FALLING);
  // pinMode(GS_INT_PIN, INPUT);
  attachInterrupt(digitalPinToInterrupt(GS_INT_PIN), handle_GS_INT, RISING);
  pinMode(VL_SHUT_PIN, OUTPUT);
  digitalWrite(VL_SHUT_PIN, HIGH);

  if (rst_rea == ESP_RST_POWERON) {
    nvs_fault_clear();
    check_vbat_voltage();
  } else {
    beep(1, 50, 50);
    uint32_t fault_count = nvs_fault_up();
    while (fault_count >= SERIOUSFAULT_COUNT) {
      sendATCommand("AT+QSCLK=1");
      ec_standard_mode();
      delay(100);
      esp_sleep_enable_timer_wakeup(1 * 3600 * 1000000LL);
      esp_light_sleep_start();
      beep(1, 50, 50);
    }
  }
  // !! 使用single唤醒,需要在库代码中屏蔽enableDRDY, 否则INT1将保持高电平
  if (!lis.begin(0x19)) {
    Serial.println("LIS3DH Couldnt start");
    beep(3, 100, 100);
    ESP.restart();
  }
  Serial.println("LIS3DH Init");
  lis.setRange(LIS3DH_RANGE_2_G);
  lis.setClick(1, CLICKTHRESHOLD);
  lis.setPerformanceMode(LIS3DH_MODE_LOW_POWER);
  delay(100);

  vl_ic_check();
  light_shutdown();
  
  MySerial.setRxTimeout(50); //5ms
  MySerial.onReceive(onSerialData, true); //timeout
  MySerial.onReceiveError(onSerialErr);

  watchdog_timer = timerBegin(0, 80, true);                     //timer 1Mhz resolution
  timerAttachInterrupt(watchdog_timer, &resetModule, true);       //attach callback
  timerAlarmWrite(watchdog_timer, (wdtTimeout * 1000), false); 
  timerAlarmEnable(watchdog_timer);

  xTaskCreatePinnedToCore(
  TaskUart
  ,  "TaskUart"   // A name just for humans
  ,  8192  // This stack size can be checked & adjusted by reading the Stack Highwater
  ,  NULL
  ,  2  // Priority, with 3 (configMAX_PRIORITIES - 1) being the highest, and 0 being the lowest.
  ,  &TaskUartHandle 
  ,  ARDUINO_RUNNING_CORE);

  xTaskCreatePinnedToCore(
  Taskmqtt
  ,  "Taskmqtt"
  ,  8192  // Stack size
  ,  NULL
  ,  1  // Priority
  ,  &TaskMQTTHandle 
  ,  ARDUINO_RUNNING_CORE);

  xBEEZTimer = xTimerCreate(
      "BuzzerTimer",                  // 定时器名称
      pdMS_TO_TICKS(200),             // 定时周期200ms
      pdTRUE,                         // 自动重载
      (void *)0,                      // 定时器ID（可以不使用）
      vBEEZTimerCallback              // 定时器回调函数
  );

  xInfoPushTimer = xTimerCreate(
      "xInfoPushTimer",               // 定时器名称
      pdMS_TO_TICKS(20000),
      pdTRUE,                         // 自动重载
      (void *)0,                      // 定时器ID（可以不使用）
      vInfoPushTimerCallback          // 定时器回调函数
  );
  if (xInfoPushTimer != NULL) {
      xTimerStart(xInfoPushTimer, 0);
  }

  xLightSleepTimer = xTimerCreate(
      "xLightSleepTimer",               // 定时器名称
      pdMS_TO_TICKS(CONFIG_INTERMIT_TIMER),
      pdTRUE,                         // 自动重载
      (void *)0,                      // 定时器ID（可以不使用）
      vLightSleepTimerCallback          // 定时器回调函数
  );

  setCpuFrequencyMhz(80);
  //打印修改后的CPU频率
  Serial.print("New CPU Frequency: ");
  Serial.print(getCpuFrequencyMhz());
  Serial.println(" MHz");
}

void loop() {
  timerWrite(watchdog_timer, 0); //reset timer (feed watchdog)
  vTaskDelay(500 / portTICK_PERIOD_MS);
}

String detect_imei_to_secret(String (*dev)[2], String imei)
{
  uint8_t i = 0;
  for(;;) {
    if (dev[i][0].equals(imei)) {
      return dev[i][1];
    } else if (dev[i][0].equals("")) {
      return "";
    }
    i++;
  }
}

void TaskUart(void *pvParameters)  // This is a task.
{
  (void) pvParameters;
  String uart_recv;
  
  for (;;)
  {
    ulTaskNotifyTake(pdTRUE, portMAX_DELAY);
    //first data copy
    uart_recv = ec_uart_interrupt_buf;
    ec_uart_interrupt_buf = "";

    if (uart_recv.indexOf("AT+GSN") != -1) {
      IMEI = extractedGSNAT(uart_recv);
      Serial.print("IMEI: "); 
      Serial.println(IMEI);
      ALI_DEVICESECRET = detect_imei_to_secret(imei_device_info, IMEI);
      ALI_DEVICENAME = IMEI;
    } else if(uart_recv.indexOf("AT+QLTS") != -1) {
      extractedQLTSAT(uart_recv);
    } else if (uart_recv.indexOf("AT+CSQ") != -1) {
      RSSI = extractedCSQAT(uart_recv);
    }

    if (uart_recv.indexOf("ERROR") != -1) {
      xTaskNotify(TaskMQTTHandle, EVENT_ATERR_FLAG, eSetBits);
    } else if (uart_recv.indexOf("OK") != -1) {
      xTaskNotify(TaskMQTTHandle, EVENT_ATOK_FLAG, eSetBits);
      //同步通知
    } else if (uart_recv.indexOf("+QMTRECV") != -1) {
      handle_mqtt_request(uart_recv);
      xTaskNotify(TaskMQTTHandle, EVENT_ATRECV_FLAG, eSetBits);
    } else if (uart_recv.indexOf("+QMTSTAT") != -1) {
      int extractedQMTSTAT = extractedNumberAT(uart_recv);
      Serial.print("+QMTSTAT: 0,");
      Serial.println(extractedQMTSTAT);
      xTaskNotify(TaskMQTTHandle, EVENT_ATSTAT_FLAG, eSetBits);
    } else if (uart_recv.indexOf("+QMTPING") != -1) {
      int extractedQMTPING = extractedNumberAT(uart_recv);
      Serial.print("+QMTPING: 0,");
      Serial.println(extractedQMTPING);
      xTaskNotify(TaskMQTTHandle, EVENT_ATPING_FLAG, eSetBits);
    } else if (uart_recv.indexOf("+QMTOPEN") != -1) {
      int extractedQMTOPEN = extractedNumberAT(uart_recv);
      Serial.print("+QMTOPEN: 0,");
      Serial.println(extractedQMTOPEN);
      xTaskNotify(TaskMQTTHandle, EVENT_ATOPEN_FLAG, eSetBits);
    } else if (uart_recv.indexOf("+QMTCONN") != -1) {
      int extractedQMTCONN = extractedNumberAT(uart_recv);
      Serial.print("+QMTCONN: 0,?,");
      Serial.println(extractedQMTCONN);
      xTaskNotify(TaskMQTTHandle, EVENT_ATCONN_FLAG, eSetBits);
    } else {
      xTaskNotify(TaskMQTTHandle, EVENT_UNNOWN_FLAG, eSetBits);
    }
  }
}


void Taskmqtt(void *pvParameters)  // This is a task.
{
  (void) pvParameters;
  uint32_t ulNotificationValue;

  for (;;)
  {
    net_config_AT();

    ulTaskNotifyTake(pdTRUE, portMAX_DELAY);
    xTaskNotifyWait(0, ULONG_MAX, &ulNotificationValue, portMAX_DELAY);
    if (ulNotificationValue & EVENT_TIMEDRUN_FLAG) {
      timerPushMQTT(false);
    }

    if (ulNotificationValue & EVENT_REPLY_FLAG) {
      timerPushMQTT(true);
    }

    if (ulNotificationValue & EVENT_FINDMY_FLAG) {
      if (xBEEZTimer != NULL) {
        xTimerStart(xBEEZTimer, 0);
      }
    }

    if (ulNotificationValue & EVENT_TIMEDRUN_FLAG) {
#if (CONFIG_INTERMIT_SLEEP == 1)
      if (xLightSleepTimer != NULL) {
        xTimerStart(xLightSleepTimer, 0);
        Serial.println("Countdown to light sleep..."); 
      }
#endif
    }
    if ((ulNotificationValue & EVENT_REPLY_FLAG) || (ulNotificationValue & EVENT_FINDMY_FLAG)) {
      if (xLightSleepTimer != NULL && xInfoPushTimer != NULL) {
        xTimerStop(xLightSleepTimer, 0);
        xTimerReset(xInfoPushTimer, 0);
        Serial.println("STOP to light sleep!"); 
      }
    }
#if (CONFIG_LATENIGHT_SLEEP == 1)
    if (latenight_sleep_flag == true) {
      // 23:00 to light sleep
      int current_hour = rtc.getHour(true);
      if ((current_hour > 0) && (current_hour < 6)) {
        enterLightSleep((6 - current_hour) * 3600 * 1000000LL);
      } else if (current_hour == 23) {
        enterLightSleep(7 * 3600 * 1000000LL);
      }
    }
#endif
  if (ec_fault_count > MAX_ERR_COUNT) {
    beep(4, 50, 50);
    ESP.restart();
  }
  }
}
